use core::{alloc::AllocError, marker::PhantomData};

use uefi_ffi::services::boot::memory::{EfiAllocateType, EfiPhysicalAddress};

use crate::BootServices;

use super::MemoryType;

pub struct Page<MT: MemoryType> {
    pub phy_addr: EfiPhysicalAddress, // page 的起始物理地址
    pub page_num: usize,
    phantom: PhantomData<MT>,
}

impl<MT: MemoryType> Page<MT> {
    pub fn free(self) {
        BootServices::take().free_pages(self.phy_addr, self.page_num);
    }
    fn allocate_pages(
        allocate_type: EfiAllocateType,
        page_num: usize,
        phy_addr: &mut EfiPhysicalAddress,
    ) -> Result<Page<MT>, AllocError> {
        let status =
            BootServices::take().allocate_pages(allocate_type, MT::MEM_TYPE, page_num, phy_addr);
        if status.is_sucess() {
            Ok(Page {
                phy_addr: phy_addr.clone(),
                page_num,
                phantom: PhantomData,
            })
        } else {
            Err(AllocError)
        }
    }
    pub fn allocate_any(page_num: usize) -> Result<Self, AllocError> {
        let mut phy_addr = 0;
        Self::allocate_pages(EfiAllocateType::AllocateAnyPages, page_num, &mut phy_addr)
    }
    pub fn allocate_max(phy_addr: EfiPhysicalAddress, page_num: usize) -> Result<Self, AllocError> {
        let mut _phy_addr = phy_addr;
        Self::allocate_pages(
            EfiAllocateType::AllocateMaxAddress,
            page_num,
            &mut _phy_addr,
        )
    }
    pub fn allocate_at(phy_addr: EfiPhysicalAddress, page_num: usize) -> Result<Self, AllocError> {
        let mut _phy_addr = phy_addr;
        Self::allocate_pages(EfiAllocateType::AllocateAddress, page_num, &mut _phy_addr)
    }
}
